﻿//
// *************************************************************************************
// WP-Framework
// 
// Developed by
// Lukas Kretschmar (lkretschmar.dev@bluewin.ch)
//
// Switzerland, Copyright (c) 2013 lkretschmar
// This content is released under the MIT License (http://opensource.org/licenses/MIT).
//     
// *************************************************************************************
//
//
			

using System;
using System.Linq;
using System.Collections.Generic;

namespace KretschIT.WP_Fx.EasyMoq.Generator.Items
{
    internal class TypeCollector : ITypeCollector
    {
        private Dictionary<Type, List<Type>> usedTypes = new Dictionary<Type, List<Type>>();
        private ITypeFinder finder = new TypeFinder();

        public void Add(Type type)
        {
            if (type == null)
            {
                return;
            }
            var types = this.GetUsedTypesCollectionOf(type);
            this.CollectUsedTypesOf(type, types);
        }

        #region UsedTypeCollection

        private List<Type> GetUsedTypesCollectionOf(Type type)
        {
            if (this.usedTypes.ContainsKey(type))
            {
                return this.usedTypes[type];
            }
            return this.CreateNewUsedTypeCollection(type);
        }

        private List<Type> CreateNewUsedTypeCollection(Type type)
        {
            var newCollection = new List<Type>();
            this.usedTypes.Add(type, newCollection);
            return newCollection;
        }

        #endregion

        #region AddTypes    

        private void CollectUsedTypesOf(Type type, List<Type> usedTypes)
        {
            this.AddSelf(type, usedTypes);
            this.AddIfNotExists(this.finder.GetGenericTypeArgumentsOf(type), usedTypes);
            this.AddIfNotExists(this.finder.GetGenericConstraintTypesOf(type), usedTypes);
            this.AddIfNotExists(this.finder.GetMethodTypesOf(type), usedTypes);
            this.AddIfNotExists(this.finder.GetPropertyTypesOf(type), usedTypes);
            this.AddIfNotExists(this.finder.GetEventTypesOf(type), usedTypes);
        }

        private void AddSelf(Type type, List<Type> usedTypes)
        {
            var t = this.GetAbstractType(type);
            this.AddIfNotExists(t, usedTypes);
        }

        private void AddIfNotExists(IEnumerable<Type> foundTypes, List<Type> usedTypes)
        {
            foreach (var t in foundTypes)
            {
                this.AddIfNotExists(t, usedTypes);
            }
        }

        private void AddIfNotExists(Type type, List<Type> usedTypes)
        {
            if (usedTypes.Contains(type))
            {
                return;
            }
            usedTypes.Add(type);
        }

        #endregion

        public bool NameOccursMoreThanOnceOf(Type type)
        {
            var abstractType = this.GetAbstractType(type);
            var filteredTypes = this.usedTypes.Values.SelectMany(v => v).Where(t => t != abstractType);
            return filteredTypes.Any(t => t.Name == abstractType.Name);
        }

        private Type GetAbstractType(Type type)
        {
            if (type.IsGenericType)
            {
                return type.GetGenericTypeDefinition();
            }
            return type;
        }

        public IEnumerable<Type> GetUsedTypesOf(Type type)
        {
            if (type == null)
            {
                return new List<Type>();
            }
            return this.GetUsedTypesCollectionOf(type);
        }
    }
}
